package com.tzm.gaflow.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.tzm.gaflow.consts.FlowNames;
import com.tzm.gaflow.dao.GaFlowGrantDao;
import com.tzm.gaflow.dao.GaFlowMainDao;
import com.tzm.gaflow.dto.GaBusinessDTO;
import com.tzm.gaflow.entity.GaFlowGrant;
import com.tzm.gaflow.entity.GaFlowInfo;
import com.tzm.gaflow.entity.GaFlowMain;
import com.tzm.gaflow.entity.GaFlowTasklog;
import com.tzm.gaflow.handler.TaskAfterHandler;
import com.tzm.gaflow.service.GaFlowService;
import com.tzm.gaflow.strategy.TaskExecuteStrategyEnum;
import com.tzm.gaflow.web.exception.BusinessException;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.util.List;


@Service
public class GaFlowServiceImpl implements GaFlowService {
    @Resource
    GaFlowGrantDao gaFlowGrantDao;
    @Resource
    GaFlowMainDao gaFlowMainDao;

    @Override
    public GaFlowTasklog postTask(GaBusinessDTO InGaBusinessDTO) {
        GaBusinessDTO gaBusinessDTO = new GaBusinessDTO();
        BeanUtil.copyProperties(InGaBusinessDTO, gaBusinessDTO);
        Assert.notNull(gaBusinessDTO, "传入的业务入参对象不能为空！");
        Assert.notNull(gaBusinessDTO.getBussinesType(), "传入的业务类型不能为空！");
        Assert.notNull(gaBusinessDTO.getBussinesKey(), "传入的业务主键不能为空！");
        Assert.notNull(gaBusinessDTO.getContent(), "传入的业务处理结果不能为空！");
        Assert.notNull(gaBusinessDTO.getAuthorityType(), "传入的权限类型不能都为空!");
        Assert.notNull(gaBusinessDTO.getAuthorityValue(), "传入的权限值不能都为空!");

        String content = gaBusinessDTO.getContent();
        String afterEventAction = "";

        if (!"RESULT_ACCEPT".equals(content) && !"RESULT_BACK".equals(content) && !"RESULT_CLOSE".equals(content)) {
            throw new BusinessException("传入的业务处理结果不正确");
        }

        GaFlowTasklog gaFlowTasklog;
        GaFlowMain gaFlowMain = new GaFlowMain()
                .selectOne(Wrappers.<GaFlowMain>lambdaQuery()
                        .eq(GaFlowMain::getBussinesKey, gaBusinessDTO.getBussinesKey())
                        .eq(GaFlowMain::getBussinesType, gaBusinessDTO.getBussinesType()));
        if (gaFlowMain == null) {//create task
            GaFlowMain newTask = this.createTask(gaBusinessDTO);
            GaFlowInfo gaFlowInfoEvent = this.getGaFlowInfoByFlowId(newTask.getFlowId());
            afterEventAction = gaFlowInfoEvent.getAfterEventAction();
            GaFlowMain gaFlowMainEvent = new GaFlowMain();
            BeanUtil.copyProperties(newTask, gaFlowMainEvent);
            gaFlowTasklog = this.nextTask(newTask, gaBusinessDTO, true);
            if (StrUtil.isNotBlank(afterEventAction)) {
                this.afterEventAction(afterEventAction, gaFlowMainEvent, gaBusinessDTO);
            }
        } else {//post task
            GaFlowInfo gaFlowInfoEvent = this.getGaFlowInfoByFlowId(gaFlowMain.getFlowId());
            afterEventAction = gaFlowInfoEvent.getAfterEventAction();
            GaFlowMain gaFlowMainEvent = new GaFlowMain();
            BeanUtil.copyProperties(gaFlowMain, gaFlowMainEvent);
            gaFlowTasklog = this.nextTask(gaFlowMain, gaBusinessDTO, false);
            if (StrUtil.isNotBlank(afterEventAction)) {
                this.afterEventAction(afterEventAction, gaFlowMainEvent, gaBusinessDTO);
            }
        }
        //log
        gaFlowTasklog.insert();
        return gaFlowTasklog;
    }

    @Override
    public List<GaFlowMain> getBussinesList(GaBusinessDTO IngaBusinessDTO) {

        GaBusinessDTO gaBusinessDTO = new GaBusinessDTO();
        BeanUtil.copyProperties(IngaBusinessDTO, gaBusinessDTO);
        Assert.notNull(gaBusinessDTO, "传入的业务入参对象不能为空！");
        Assert.notNull(gaBusinessDTO.getAuthorityType(), "传入的权限类型不能都为空!");
        Assert.notNull(gaBusinessDTO.getAuthorityValue(), "传入的权限值不能都为空!");
        List<GaFlowMain> bussinesList = gaFlowMainDao.getBussinesList(gaBusinessDTO.getBussinesType(), gaBusinessDTO.getAuthorityType(), gaBusinessDTO.getAuthorityValue());
        return bussinesList;
    }

    private void afterEventAction(String afterEventAction, GaFlowMain gaFlowMainEvent, GaBusinessDTO gaBusinessDTO) {

        GaBusinessDTO gaBusinessDTO1 = new GaBusinessDTO();
        BeanUtil.copyProperties(gaBusinessDTO, gaBusinessDTO1);
        Class class1;
        try {
            class1 = this.getClass().getClassLoader().loadClass(afterEventAction);
        } catch (ClassNotFoundException e) {
            throw new BusinessException("流程指定的后置事件类不存在:" + afterEventAction);
        }
        if (TaskAfterHandler.class.isAssignableFrom(class1)) {
            TaskAfterHandler taskAfterHandler = null;
            try {
                taskAfterHandler = (TaskAfterHandler) class1.newInstance();
            } catch (Exception exception) {
                throw new BusinessException("创建类" + afterEventAction + "的实例失败！", exception);
            }
            taskAfterHandler.taskAfterHandle(gaFlowMainEvent, gaBusinessDTO1);
        } else {
            throw new BusinessException("流程指定的后置事件类" + afterEventAction + "没有实现TaskAfterHandler接口!");
        }
    }

    private GaFlowInfo getFirstGaFlowInfo(String bussinesType) {
        GaFlowInfo gaFlowInfo = new GaFlowInfo()
                .selectOne(Wrappers.<GaFlowInfo>lambdaQuery()
                        .eq(GaFlowInfo::getBussinesType, bussinesType)
                        .eq(GaFlowInfo::getActive, FlowNames.FLOW_ACTIVE)
                        .isNull(GaFlowInfo::getPreviousFlowId));
        return gaFlowInfo == null ? null : gaFlowInfo;

    }

    private GaFlowInfo getGaFlowInfoByFlowId(String flowId) {
        GaFlowInfo gaFlowInfo = new GaFlowInfo().selectById(flowId);
        return gaFlowInfo;
    }

    private GaFlowMain createTask(GaBusinessDTO gaBusinessDTO) {

        if (!"RESULT_ACCEPT".equals(gaBusinessDTO.getContent())) {
            throw new BusinessException("开始流程节点传入的处理结果不正确！");
        }

        GaFlowMain gaFlowMain = new GaFlowMain();

        GaFlowInfo firstGaFlowInfo = this.getFirstGaFlowInfo(gaBusinessDTO.getBussinesType());
        if (ObjectUtil.isEmpty(firstGaFlowInfo)) {
            throw new BusinessException("没有找到业务类型【" + gaBusinessDTO.getBussinesType() + "】流程的开始任务节点！");
        }
        if (!this.checkGrant(firstGaFlowInfo.getFlowId(), gaBusinessDTO.getAuthorityType(), gaBusinessDTO.getAuthorityValue())) {
            throw new BusinessException("没有权限处理当前流程任务节点！");
        }
        gaFlowMain.setBussinesKey(gaBusinessDTO.getBussinesKey());
        gaFlowMain.setBussinesType(gaBusinessDTO.getBussinesType());
        gaFlowMain.setFlowId(firstGaFlowInfo.getFlowId());
        gaFlowMain.setFlowName(firstGaFlowInfo.getFlowName());
        gaFlowMain.setPreviousFlowId(firstGaFlowInfo.getPreviousFlowId());
        gaFlowMain.setPreviousFlowName(firstGaFlowInfo.getPreviousFlowName());
        gaFlowMain.setNextFlowId(firstGaFlowInfo.getNextFlowId());
        gaFlowMain.setNextFlowName(firstGaFlowInfo.getNextFlowName());
        gaFlowMain.setApplyTime(DateUtil.parseDateTime(DateUtil.now()));
        gaFlowMain.setTaskState(FlowNames.TASKSTATE_START);
        gaFlowMain.setRemark("流程任务启动");
        gaFlowMain.setAuditFlag("1");
        gaFlowMain.setContent(gaBusinessDTO.getContent());
        gaFlowMain.setCreateTime(DateUtil.parseDateTime(DateUtil.now()));
        gaFlowMain.insert();
        return gaFlowMain;
    }

    private GaFlowTasklog nextTask(GaFlowMain gaFlowMain, GaBusinessDTO gaBusinessDTO, Boolean firstSkip) {

        if (gaFlowMain.getTaskState().equals(FlowNames.TASKSTATE_END)) {
            throw new BusinessException("业务类型为【" + gaBusinessDTO.getBussinesType() + "】,业务主键为【" + gaBusinessDTO.getBussinesKey() + "】流程已经终止！");
        }
        if (!firstSkip) {
            if (!this.checkGrant(gaFlowMain.getFlowId(), gaBusinessDTO.getAuthorityType(), gaBusinessDTO.getAuthorityValue())) {
                throw new BusinessException("没有权限处理当前流程任务节点！");
            }
        }
        return TaskExecuteStrategyEnum.match(gaBusinessDTO.getContent(), null).getRouter().route(gaBusinessDTO, gaFlowMain);
    }

    private Boolean checkGrant(String flowId, String authorityType, List<String> authorityValue) {

        GaFlowGrant gaFlowGrant = gaFlowGrantDao.getGrantByFlow(flowId, authorityType, authorityValue);
        return gaFlowGrant == null ? false : true;
    }

}
